package com.ht.api.db.metadata;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.sql.DataSource;

import com.ht.api.db.Db;
import com.ht.api.db.DbConfig;
import com.ht.api.db.DbList;
import com.ht.api.db.Entity;
import com.ht.api.db.SqlParser;
import com.ht.api.db.bean.DbSql;
import com.ht.api.db.util.HtUtil;

import cn.hutool.core.date.DateUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 抽象的数据库元数据管理器
 * @author asq
 * @createTime 2024年12月4日 13:50:08
 */
@Slf4j
public abstract class AbstractMetadataManager{
	/** 系统数据源 */
	protected static DataSource sysDS = DbConfig.useSysDb();

	/**
	 * 新增
	 * @param entity 表或列的元数据
	 */
	public final void add(Entity entity) {
		executeSql(addSchema(entity), addData(entity), getTargetDSArr(entity));
	}

	/**
	 * 修改
	 * @param entity 表或列的元数据
	 */
	public final void modify(Entity entity) {
		executeSql(modifySchema(entity), modifyData(entity), getTargetDSArr(entity));
	}
	
	/**
	 * 删除
	 * @param entity 表或列的元数据
	 */
	public final void del(Entity entity) {
		executeSql(delSchema(entity), delData(entity), getTargetDSArr(entity));
	}
	
	/**
	 * 复制表
	 * @param oldTableCode 旧表名
	 * @param newTableCode 新表名
	 * @param newTableAlias 新表别名
	 * @param newTableName 新表注释
	 */
	public final void copyTable(String oldTableCode, String newTableCode, String newTableAlias, String newTableName) {
		// 获取要操作的目标数据源
		List<DataSource> dsList = null;
		if (DbConfig.isTenant(oldTableCode)) {
			dsList = new ArrayList<>(DbConfig.tenantDSMap.values());
		} else {
			dsList = Arrays.asList(DbConfig.useSysDb());
		}
		
		// 复制表结构
		StringBuilder sb = new StringBuilder();
		sb.append(String.format("CREATE TABLE `%s` LIKE `%s`;", newTableCode, oldTableCode));
		sb.append(String.format("ALTER TABLE `%s` MODIFY COMMENT '%s';", newTableCode, newTableName));
		for (DataSource ds : dsList) {
			try {
				Db.execute(ds, sb.toString());
			} catch (Exception e) {
				log.error("[meta]操作租户数据库发生异常：{}", e.getMessage(), e);
			}
		}
		
		// db_table插入数据
		sb = new StringBuilder(); 
		sb.append("INSERT INTO db_table(`main_id`,`sys_time`,`sys_deleted`,`sys_creator`,`code`,`alias`,`name`,`tenant`,`group_id`)");
		sb.append("select ?,?,sys_deleted,sys_creator,?,?,?,tenant,group_id from db_table where code=?");
		Db.execute(sb.toString(), HtUtil.getId(), DateUtil.now(), newTableCode, newTableAlias, newTableName, oldTableCode);
		
		// db_column插入数据
		DbList columnList = Db.createSqlParser("db_column").where("table_code", oldTableCode).query();
		columnList.stream().forEach(c -> {
			// 这里注意：要新new一个Entity，才能正常插入数据库
			Entity entity = Db.createEntity("db_column");
			entity.set("main_id", HtUtil.getIdLong());
			entity.set("sys_time", c.getStr("sys_time"));
			entity.set("sys_deleted", c.getInt("sys_deleted"));
			entity.set("sys_creator", c.getInt("sys_creator"));
			entity.set("table_code", newTableCode);
			entity.set("code", c.getStr("code"));
			entity.set("name", c.getStr("name"));
			entity.set("type", c.getStr("type"));
			entity.set("fun", c.getStr("fun"));
			entity.set("default_value", c.getStr("default_value"));
			entity.set("primary_key", c.getInt("primary_key"));
			entity.set("remark", c.getStr("remark"));
			entity.set("order_num", c.getInt("order_num"));
			entity.set("dict_id", c.getInt("dict_id"));
			entity.set("mapper_config", c.getStr("mapper_config"));
			Db.insert(entity);
		});
	}
	
	/**
	 * 设置主键
	 * @param entity
	 */
	public final List<Entity> setPrimary(Entity entity, boolean enabled) {
		String mainId = entity.getStr("main_id");
		String tableCode = entity.getStr("table_code");
		String code = entity.getStr("code");
		
		// 获取要操作的目标数据源
		List<DataSource> dsList = null;
		if (DbConfig.isTenant(tableCode)) {
			dsList = new ArrayList<>(DbConfig.tenantDSMap.values());
		} else {
			dsList = Arrays.asList(DbConfig.useSysDb());
		}
		
		// 检查是否有数据（注意如果是租户表，需要检查所有租户库）
		for (DataSource ds : dsList) {
		    long total = Db.createSqlParser(tableCode).count(ds);
		    if (total > 0) {
		        throw new RuntimeException("已存在数据，不能更改主键！");
		    }
		}
		
		if (enabled) {
			// 查询目前的主键最大值+1，成为新的主键顺序值
			StringBuilder sb = new StringBuilder();
			sb.append("select if(max(primary_key) is null,1,max(primary_key)+1) from db_column ");
			sb.append("where table_code=? and main_id!=? and primary_key is not null");
			int nextPK = Db.val(sb.toString(), tableCode, mainId);
			modifyPrimary(entity, nextPK);
		} else {
			// 重新排序后写入，保证主键顺序值为1，2，3这种递增
			int key = 1;
			for (Entity primaryField : getPrimaryList(tableCode)) {
				if (primaryField.getStr("code").equals(code)) {
					modifyPrimary(primaryField, 0);
				} else {
					modifyPrimary(primaryField, key++);
				}
			}
			
		}
		
		// 返回最新的主键列表
		return getPrimaryList(tableCode);
	}
	
	/**
	 * 查询
	 * @param sqlParser 表或列的元数据
	 */
	public abstract DbList find(SqlParser sqlParser);

	/**
	 * 新增表数据，执行sql
	 * @param entity
	 */
	protected abstract DbSql addData(Entity entity);
	
	/**
	 * 新增元数据，执行DDL
	 * @param entity
	 */
	protected abstract DbSql addSchema(Entity entity);
	
	/**
	 * 修改表数据，执行sql
	 * @param entity
	 */
	protected abstract DbSql modifyData(Entity entity);
	
	/**
	 * 修改元数据，执行DDL
	 * @param entity
	 */
	protected abstract DbSql modifySchema(Entity entity);
	
	/**
	 * 删除表数据，执行sql
	 * @param entity
	 */
	protected abstract DbSql delData(Entity entity);
	
	/**
	 * 删除元数据，执行DDL
	 * @param entity
	 */
	protected abstract DbSql delSchema(Entity entity);
	
	/**
	 * 判断是表操作还是列操作
	 * @return
	 */
	protected abstract boolean isTable();
	
	/**
	 * 执行元数据sql和表数据sql
	 * @param sqlSchema 元数据sql对象
	 * @param sqlData 表数据sql对象
	 * @param dsList 元数据sql对应的数据源列表（维护租户表时，需要在多个租户数据源上执行）
	 */
	private void executeSql(DbSql sqlSchema, DbSql sqlData, DataSource... dsList) {
		// 在指定库中执行元数据sql，如果未指定，则直接在系统库中执行
		if (!sqlSchema.getSql().isBlank()) {
			if (dsList != null && dsList.length > 0) {
				for (DataSource ds : dsList) {
					try {
						Db.execute(ds, sqlSchema.getSql(), sqlSchema.getParamsList());
					} catch (Exception e) {
						log.error("[meta]操作租户数据库发生异常：{}", e.getMessage(), e);
					}
				}
			} else {
				Db.execute(sysDS, sqlSchema.getSql(), sqlSchema.getParamsList());
			}
		}
		
		// 在系统库中执行表数据sql
		Db.execute(sysDS, sqlData.getSql(), sqlData.getParamsList());
	}
	
	/**
	 * 根据当前操作的目标表是否是租户表，返回需要操作的租户数据源列表
	 * @param entity
	 * @return
	 */
	private DataSource[] getTargetDSArr(Entity entity) {
		// 获取所有的租户数据源
		DataSource[] dsArr = null;
		if (isTenantOpt(entity)) {
		    dsArr = DbConfig.tenantDSMap.values().toArray(new DataSource[0]);
		}
		
		return dsArr;
	}
	
	/**
	 * 判断是否是租户表
	 * @param entity
	 * @return
	 */
	private boolean isTenantOpt(Entity entity) {
		// 新增表时，会传递tenant字段
		if (entity.containsKey("tenant")) {
			return entity.getInt("tenant") == 1;
		}
		
		// 获取表名，根据表名去查询该表是否是租户表
		String tableCode = isTable() ? entity.getStr("code") : entity.getStr("table_code");
		
		return DbConfig.isTenant(tableCode);
	}
	
	/**
	 * 返回某个表的主键顺序值列表
	 * @param tableCode 表名
	 * @return
	 */
	private static List<Entity> getPrimaryList(String tableCode){
		String sql = "select * from db_column where primary_key is not null and primary_key>0 and table_code=? order by primary_key";
		return Db.query(sql, tableCode);
	}
	
	/**
	 * 修改一个列数据的主键顺序值
	 * @param entity 列数据
	 * @param primaryKey 主键顺序值
	 */
	private static void modifyPrimary(Entity entity, int primaryKey) {
		String mainId = entity.getStr("main_id");
		Db.execute("update db_column set primary_key=? where main_id=?", primaryKey, mainId);
	}
}
